#ifndef __TOY__RENDERER__H__
#define __TOY__RENDERER__H__

#include <iostream>
#include <assert.h>
#include <math.h>
#include <initializer_list>
#include <map>
#include <functional>
#include "ToyMath.h"
#include "ToyBmp.h"

namespace ToyCube {

Mat<4, 4, float> RotateMat(float x, float y, float z, float theta);
Mat<4, 4, float> SacleMat(float x, float y, float z);
Mat<4, 4, float> TranslateMat(float x, float y, float z);
Mat<4, 4, float> CameraMat(const Vec<3, float> &up, const Vec<3, float> &eye, const Vec<3, float> &at);
Mat<4, 4, float> OrthProjMat(float eye_fov, float aspect, float near, float far);
Mat<4, 4, float> PersProjMat(float eye_fov, float aspect, float near, float far);
Vec<4, float> TransformCoordinate(Mat<4, 4, float> &mat, Vec<4, float> &vec);

class Triangle{
public:
    Triangle(){}
    ~Triangle(){}
    
    Vec<3, float> a() const { return vertex[0]; }
    Vec<3, float> b() const { return vertex[1]; }
    Vec<3, float> c() const { return vertex[2]; }

    void SetVertex(int index, Vec<3, float> &vec){ vertex[index] = vec; }
    
    void SetColor(int index, uint32_t argb){
        color[index][0] = (argb & 0xff) / 255.0; //b
        color[index][1] = ((argb >> 8) & 0xff) / 255.0; //g
        color[index][2] = ((argb >> 16) & 0xff) / 255.0; //r
        color[index][3] = ((argb >> 24) & 0xff) / 255.0; //a
    }
    
    void SetTexCoords(int index, float u, float v){
        tex_coords[index][0] = u;
        tex_coords[index][1] = v;
    }
    
    void SetNormal(int index, Vec<3, float> &vec) { normal[index] = vec; }
    
    Vec<3, float> vertex[3];
    Vec<4, float> color[4];
    Vec<2, float> tex_coords[3];
    Vec<3, float> normal[3];
};

struct ShaderContext {
    std::map<int, float> varying_float;    // 浮点数 varying 列表
    std::map<int, Vec<2, float>> varying_vec2f;    // 二维矢量 varying 列表
    std::map<int, Vec<3, float>> varying_vec3f;    // 三维矢量 varying 列表
    std::map<int, Vec<4, float>> varying_vec4f;    // 四维矢量 varying 列表
};

struct Vertex{
    
    ShaderContext context;
    Vec<4, float> pos;
    Vec<2, float> screen_pos_f;
    Vec<2, int> screen_pos_i;
    float rhw;
    
    void Clear(){
        context.varying_float.clear();
        context.varying_vec2f.clear();
        context.varying_vec3f.clear();
        context.varying_vec4f.clear();
    }
};

class ToyRenderer{
    
public:
    ToyRenderer() : _frame_buffer(nullptr),
                    _draw_outline(false),
                    _antialias_enable(true){}
    ~ToyRenderer(){}

    void Init(int width, int height){
        _fb_width = width;
        _fb_height = height;
        _frame_buffer = new Bitmap(width, height);
        
        _depth_buffer = new float*[height];
        for(int i=0; i<height; i++){
            _depth_buffer[i] = new float[width];
            for(int j=0; j<width; j++){
                _depth_buffer[i][j] = -999999999;
            }
        }
    }
    
    Bitmap *GetFrameBuffer(){
        return _frame_buffer;
    }
    
    void DrawOutline(bool draw){
        _draw_outline = draw;
    }
    
    void Deinit(){
        if(_frame_buffer){
            delete _frame_buffer;
        }
        if(_depth_buffer){
            for(int i=0; i<_fb_height; i++){
                if(_depth_buffer[i]){
                    delete[] _depth_buffer[i];
                    _depth_buffer[i] = nullptr;
                }
            }
            delete _depth_buffer;
            _depth_buffer = nullptr;
        }
    }
    
    void SetVertxShader(std::function<Vec<4, float>(int index, ShaderContext &output)> vertex_shader){
        _vertex_shader = vertex_shader;
    }
    
    void SetFragmentShader(std::function<Vec<4, float>(ShaderContext &input)> fragment_shader){
        _fragment_shader = fragment_shader;
    }
    
    void Draw(){
        
        Vertex vertex[3];
        for(int i=0; i<3; i++){
            vertex[i].Clear();
            //顶点着色器传入经过MVP变换的顶点坐标
            if(_vertex_shader){
                vertex[i].pos = _vertex_shader(i, vertex[i].context);
            }
            vertex[i].rhw = 1.0 / vertex[i].pos[3]; // 1/w
            vertex[i].pos *= vertex[i].rhw;
            
//            std::cout<<"normalize"<<std::endl;
//            vertex[i].pos.Print();
            
            //屏幕x点
            vertex[i].screen_pos_f[0] = (vertex[i].pos[0]+1.0f) * _fb_width * 0.5;
            //屏幕y点
            vertex[i].screen_pos_f[1] = (1.0f-vertex[i].pos[1]) * _fb_height * 0.5;
            
            // 整数屏幕坐标：加 0.5 的偏移取屏幕像素方格中心对齐
            vertex[i].screen_pos_i[0] = vertex[i].screen_pos_f[0] + 0.5;
            vertex[i].screen_pos_i[1] = vertex[i].screen_pos_f[1] + 0.5;
        }
        
        std::cout<<"screen"<<std::endl;
        vertex[0].screen_pos_i.Print();
        vertex[1].screen_pos_i.Print();
        vertex[2].screen_pos_i.Print();
        
        if(_draw_outline){
            //绘制三角形轮廓
            DrawLine(vertex[0].screen_pos_i[0], vertex[0].screen_pos_i[1], vertex[1].screen_pos_i[0], vertex[1].screen_pos_i[1], 0xffffffff);
            DrawLine(vertex[1].screen_pos_i[0], vertex[1].screen_pos_i[1], vertex[2].screen_pos_i[0], vertex[2].screen_pos_i[1], 0xffffffff);
            DrawLine(vertex[2].screen_pos_i[0], vertex[2].screen_pos_i[1], vertex[0].screen_pos_i[0], vertex[0].screen_pos_i[1], 0xffffffff);
        }
        
        //求出三个顶点屏幕x坐标最大值与最小值
        auto x_info = Compair3(vertex[0].screen_pos_i[0], vertex[1].screen_pos_i[0], vertex[2].screen_pos_i[0]);
        int min_x = x_info.first;
        min_x = Mid3(min_x, 0, _fb_width);
        int max_x = x_info.second;
        max_x = Mid3(max_x, 0, _fb_width);
        //求出三个顶点屏幕y坐标最大值与最小值
        auto y_info = Compair3(vertex[0].screen_pos_i[1], vertex[1].screen_pos_i[1], vertex[2].screen_pos_i[1]);
        int min_y = y_info.first;
        min_y = Mid3(min_y, 0, _fb_height);
        int max_y = y_info.second;
        max_y = Mid3(max_y, 0, _fb_height);
        
        //遍历三角形外接矩形
        for(int y=min_y; y<=max_y; y++){
            for(int x=min_x; x<=max_x; x++){
                
                //计算当前点作为重心坐标的分开的三个三角形的面积
                Vec<2, float> vec_p = {(float)(x+0.5), (float)(y+0.5)};
                Vec<2, float> vec0_p = SubVec(vec_p, vertex[0].screen_pos_f);
                Vec<2, float> vec1_p = SubVec(vec_p, vertex[1].screen_pos_f);
                Vec<2, float> vec2_p = SubVec(vec_p, vertex[2].screen_pos_f);
                
                Vec<2, float> vec_0_1 = SubVec(vertex[1].screen_pos_f, vertex[0].screen_pos_f);
                Vec<2, float> vec_1_2 = SubVec(vertex[2].screen_pos_f, vertex[1].screen_pos_f);
                Vec<2, float> vec_2_0 = SubVec(vertex[0].screen_pos_f, vertex[2].screen_pos_f);
                
                float s2 = CrossMutVec2(vec_0_1, vec0_p); //2顶点正对三角形
                float s0 = CrossMutVec2(vec_1_2, vec1_p); //0顶点正对三角形
                float s1 = CrossMutVec2(vec_2_0, vec2_p); //1顶点正对三角形
                
                //判断点在三角形内部
                if((s0>0 && s1>0 && s2>0) || (s0<0 && s1<0 && s2<0)){
                    s0 = abs(s0);
                    s1 = abs(s1);
                    s2 = abs(s2);
                    //三角形总面积
                    float s = s0 + s1 + s2;
                    //当前点在三个顶点中所占比重
                    float k0 = s0/s;
                    float k1 = s1/s;
                    float k2 = s2/s;
                    
                    //float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                    //float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                    //z_interpolated *= w_reciprocal;
                    //rhw插值
                    float rhw = vertex[0].rhw * k0 + vertex[1].rhw * k1 + vertex[2].rhw * k2;
                    //根据插值的rhw获取当前点的w
                    float w = 1.0 / ((rhw != 0.0f) ? rhw : 1);
                    //3个顶点z归一化坐标插值
//                    float z = k0 * vertex[0].pos[3] * vertex[0].rhw + k1 * vertex[1].pos[3] * vertex[1].rhw + k2 * vertex[2].pos[3] * vertex[2].rhw;
                    float z = k0 * vertex[0].pos[2] * vertex[0].rhw + k1 * vertex[1].pos[2] * vertex[1].rhw + k2 * vertex[2].pos[2] * vertex[2].rhw;
                    //还原插值完毕后得到的z坐标 用于深度判断
                    z = z * w;
                    
                    if(z < _depth_buffer[y][x])
                        continue;
                    _depth_buffer[y][x] = z;
                    
                    //得到三角形三个点对当前点影响的比例系数 插值就用这些系数
                    float c0 = vertex[0].rhw * k0 * w;
                    float c1 = vertex[1].rhw * k1 * w;
                    float c2 = vertex[2].rhw * k2 * w;
                    
                    //拿着c0 c1 c2 将三角形三个点的每个属性分别插值，得到当前点的对应属性
                    ShaderContext &ctx0 = vertex[0].context;
                    ShaderContext &ctx1 = vertex[1].context;
                    ShaderContext &ctx2 = vertex[2].context;
                    //插值结果存储到这里 抛给fragment shader
                    ShaderContext ctx_curr;
                    
                    //插值该顶点的所有二维属性 常见uv坐标插值 插值完毕后在fragmentshader中
                    //处理每个点的时候使用
                    for(auto &it : ctx0.varying_vec2f){
                        int key = it.first;
                        Vec<2, float> vec0 = ctx0.varying_vec2f[key];
                        Vec<2, float> vec1 = ctx1.varying_vec2f[key];
                        Vec<2, float> vec2 = ctx2.varying_vec2f[key];
                        
                        ctx_curr.varying_vec2f[key] = vec0*c0 + vec1*c1 + vec2*c2;
                    }
                    for(auto &it : ctx0.varying_vec3f){
                        int key = it.first;
                        Vec<3, float> vec0 = ctx0.varying_vec3f[key];
                        Vec<3, float> vec1 = ctx1.varying_vec3f[key];
                        Vec<3, float> vec2 = ctx2.varying_vec3f[key];
                        
                        ctx_curr.varying_vec3f[key] = vec0*c0 + vec1*c1 + vec2*c2;
                    }
                    //插值该顶点的所有二维属性 常见颜色插值 法线插值
                    for(auto &it : ctx0.varying_vec4f){
                        int key = it.first;
                        Vec<4, float> vec0 = ctx0.varying_vec4f[key];
                        Vec<4, float> vec1 = ctx1.varying_vec4f[key];
                        Vec<4, float> vec2 = ctx2.varying_vec4f[key];
                        
                        ctx_curr.varying_vec4f[key] = vec0*c0 + vec1*c1 + vec2*c2;
                    }
                    
                    Vec<4, float> color;
                    if(_fragment_shader){
                        color = _fragment_shader(ctx_curr);
                    }
                    
                    float k_antialias = 1;
                    if(_antialias_enable){
                        k_antialias = Antialias_MSAA4(vec_p, vertex);
                    }
                    
                    for(int i=0; i<4; i++){
                        if(color[i] > 1) color[i] = 1;
                        else if(color[i] < 0) color[i] = 0;
                    }
                    
                    uint32_t b = ((uint8_t)(k_antialias * color[0]*255)) & 0x000000ff;
                    uint32_t g = ((uint8_t)(k_antialias * color[1]*255) << 8) &0x0000ff00;
                    uint32_t r = ((uint8_t)(k_antialias * color[2]*255) << 16)& 0x00ff0000;
                    uint32_t a = 0xff000000;//((uint8_t)(color[3]*255) << 24)& 0xff000000;
                    uint32_t color_i = a | r | g | b;
                
                    _frame_buffer->SetPixel(x, y, color_i);
                }
            }
        }
    }
    
    /**
         pos_center 当前重心点
         vertex[3]  三角形顶点
     */
    float Antialias_MSAA4(Vec<2, float> &pos_center, Vertex vertex[3]){
        
        Vec<2, float> drec_vec[] = {{-0.25f, -0.25f}, {-0.25f, 0.25}, {0.25f, -0.25f}, {0.25f, 0.25f}};
        int cover_cnt = 0;
        for(int i=0; i<4; i++){
            //分别检查每个vec_p点是否在三角形内
            Vec<2, float> vec_p = pos_center + drec_vec[i];
            
            Vec<2, float> vec0_p = SubVec(vec_p, vertex[0].screen_pos_f);
            Vec<2, float> vec1_p = SubVec(vec_p, vertex[1].screen_pos_f);
            Vec<2, float> vec2_p = SubVec(vec_p, vertex[2].screen_pos_f);
            
            Vec<2, float> vec_0_1 = SubVec(vertex[1].screen_pos_f, vertex[0].screen_pos_f);
            Vec<2, float> vec_1_2 = SubVec(vertex[2].screen_pos_f, vertex[1].screen_pos_f);
            Vec<2, float> vec_2_0 = SubVec(vertex[0].screen_pos_f, vertex[2].screen_pos_f);
            
            float s2 = CrossMutVec2(vec_0_1, vec0_p); //2顶点正对三角形
            float s0 = CrossMutVec2(vec_1_2, vec1_p); //0顶点正对三角形
            float s1 = CrossMutVec2(vec_2_0, vec2_p); //1顶点正对三角形
            
            if((s0>0 && s1>0 && s2>0) || (s0<0 && s1<0 && s2<0)){
                //在三角形内 覆盖点+1
                cover_cnt++;
            }
        }
        
        return cover_cnt/4.0f;
    }
    
    void DrawLine(int x1, int y1, int x2, int y2, uint32_t color){
        _frame_buffer->DrawLine(x1, y1, x2, y2, color);
    }
    
    void ClearColor(uint32_t color){
        _frame_buffer->Fill(color);
    }
    
    void ClearDepthBuffer(){
        
        if(_depth_buffer){
            for(int i=0; i<_fb_height; i++){
                _depth_buffer[i] = new float[_fb_width];
                for(int j=0; j<_fb_width; j++){
                    _depth_buffer[i][j] = -999999999;
                }
            }
        }
    }
    
    inline std::pair<int, int> Compair3(int a1, int a2, int a3){
        int max = a1, min = a1;
        if(a2 > max){
            max = a2;
        }else if(a2 < min){
            min = a2;
        }
        if(a3 > max){
            max = a3;
        }else if(a3 < min){
            min = a3;
        }
        return std::make_pair(min, max);
    }
    
    inline bool GetAntiAliasingEnable(){
        return _antialias_enable;
    }
    
    inline void SetAntiAliasingEnable(bool enable){
        _antialias_enable = enable;
    }
    
    void DrawArrays(int mode, int first, int count){
        
    }
        
    void SaveBmp(std::string path){
        _frame_buffer->SaveFile(path.c_str());
    }
    
    Bitmap* _frame_buffer;
    float **_depth_buffer;
    
    int _fb_width, _fb_height;
    bool _draw_outline;
    bool _antialias_enable;
    
    std::function<Vec<4, float>(int , ShaderContext&)> _vertex_shader;
    std::function<Vec<4, float>(ShaderContext &)> _fragment_shader;
};

}

#endif
